package org.nifi.processors.config;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.http.HttpInterceptor;
import cn.hutool.http.HttpRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nifi.processors.utils.JsonUtils;

import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

@Slf4j
@RequiredArgsConstructor
public class SignHttpInterceptor implements HttpInterceptor<HttpRequest> {

    private final DataAccessProperties properties;

    @Override
    public void process(HttpRequest httpRequest) {
        if (log.isDebugEnabled()) {
            log.debug("请求地址：{}", httpRequest.getUrl());
        }
        Map<String, String> headers = new LinkedHashMap<>();
        // 一次性请求
        String caNonce = IdUtil.simpleUUID();
        // 时间戳
        String caTimestamp = StrUtil.toString(System.currentTimeMillis());
        // 生成16位sm4的key
        String key = StrUtil.subPre(IdUtil.simpleUUID(), 16);
        // url 路径
        String urlPath = getUrlPath(httpRequest);
        // token信息
        String token = httpRequest.header(DataAccessConst.AUTHORIZATION);
        // 加密key
        String encryptKey = encryptKey(key);
        // 加密数据
        String encryptData = encryptData(httpRequest.form(), key);

        // 清空form请求
        MapUtil.clear(httpRequest.form());
        if (StrUtil.isNotBlank(encryptData)) {
            httpRequest.form().put(DataAccessConst.DATA, encryptData);
        }

        // 获取验签原始值
        String signOriginal = getSignOriginal(caTimestamp, caNonce, encryptKey, urlPath, encryptData, token);
        String sign = SmUtil.sm3(signOriginal);

        if (log.isDebugEnabled()) {
            log.debug("验签原始值：{}", signOriginal);
            log.debug("验签sign值：{}", sign);
        }

        headers.put(DataAccessConst.X_CA_APPID, properties.getAppId());
        headers.put(DataAccessConst.X_CA_KEY, encryptKey);
        headers.put(DataAccessConst.X_CA_NONCE, caNonce);
        headers.put(DataAccessConst.X_CA_TIMESTAMP, caTimestamp);
        headers.put(DataAccessConst.X_CA_SIGN, sign);

        if (log.isDebugEnabled()) {
            log.debug("请求体参数：{}", JsonUtils.serializeJson(httpRequest.form()));
            log.debug("请求头参数：{}", JsonUtils.serializeJson(headers));
        }


        httpRequest.addHeaders(headers);
    }

    private String encryptKey(String key) {
        if (StrUtil.isBlank(key)) {
            return null;
        }
        return SmUtil.sm2(null, properties.getPublicKey()).encryptHex(key, KeyType.PublicKey);
    }

    private String encryptData(Map<String, Object> form, String key) {
        if (MapUtil.isEmpty(form)) {
            return null;
        }
        String data = StrUtil.toStringOrNull(form.get(DataAccessConst.DATA));
        if (StrUtil.isBlank(data)) {
            return null;
        }
        return SmUtil.sm4(key.getBytes(StandardCharsets.UTF_8)).encryptHex(data);
    }

    public String getSignOriginal(String timestamp, String nonce, String encryptKey, String url, String encryptData, String token) {
        StringBuilder builder = new StringBuilder();
        builder.append(properties.getAppId())
                .append(DataAccessConst.SIGN_SEGMENT).append(timestamp)
                .append(DataAccessConst.SIGN_SEGMENT).append(nonce)
                .append(DataAccessConst.SIGN_SEGMENT).append(encryptKey)
                .append(DataAccessConst.SIGN_SEGMENT).append(url);
        if (StrUtil.isNotEmpty(encryptData)) {
            String bodySm3 = SmUtil.sm3("{\"data\":\"" + encryptData + "\"}");
            builder.append(DataAccessConst.SIGN_SEGMENT).append(bodySm3);
        }
        if (StrUtil.isNotEmpty(token)) {
            builder.append(DataAccessConst.SIGN_SEGMENT).append(token);
        }
        return builder.toString();
    }

    private String getUrlPath(HttpRequest httpRequest) {
        UrlBuilder urlBuilder = (UrlBuilder) ReflectUtil.getFieldValue(httpRequest, "url");
        return Objects.isNull(urlBuilder) ? StrUtil.EMPTY : StrUtil.toString(urlBuilder.getPath());
    }
}
